]> git.neil.brown.name Git - wiggle.git/blob - wiggle.c
Update version to 0.9.1
[wiggle.git] / wiggle.c
1 /*
2  * wiggle - apply rejected patches
3  *
4  * Copyright (C) 2003 Neil Brown <neilb@cse.unsw.edu.au>
5  * Copyright (C) 2010 Neil Brown <neilb@suse.de>
6  *
7  *
8  *    This program is free software; you can redistribute it and/or modify
9  *    it under the terms of the GNU General Public License as published by
10  *    the Free Software Foundation; either version 2 of the License, or
11  *    (at your option) any later version.
12  *
13  *    This program is distributed in the hope that it will be useful,
14  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *    GNU General Public License for more details.
17  *
18  *    You should have received a copy of the GNU General Public License
19  *    along with this program; if not, write to the Free Software Foundation, Inc.,
20  *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  *
22  *    Author: Neil Brown
23  *    Email: <neilb@suse.de>
24  */
25
26 /*
27  * Wiggle is a tool for working with patches that don't quite apply properly.
28  * It provides functionality similar to 'diff' and 'merge' but can
29  * work at the level of individual words thus allowing the merging of
30  * two changes that affect the same line, but not the same parts of that line.
31  *
32  * Wiggle can also read patch and merge files.  Unlike 'merge' it does not
33  * need to be given three separate files, but can be given a file and a patch
34  * and it will extract the pieces of the two other files that it needs from
35  * the patch.
36  *
37  * Wiggle performs one of three core function:
38  *   --extract -x    extract part of a patch or merge file
39  *   --diff -d       report differences between two files
40  *   --merge -m      merge the changes between two files into a third file
41  *
42  * This is also a --browse (-B) mode which provides interactive access
43  * to the merger.
44  *
45  * To perform these, wiggle requires 1, 2, or 3 input streams respectively.
46  * I can get these from individual files, from a diff (unified or context) or
47  * from a merge file.
48  *
49  * For merge:
50  *    If one file is given, it is a merge file (output of 'merge').
51  *    If two files are given, the second is assumed to be a patch,
52  *         the first is a normal file.
53  *    If three files are given, they are taken to be normal files.
54  *
55  * For diff:
56  *    If one file is given, it is a patch
57  *    If two files are given, they are normal files.
58  *
59  * For extract:
60  *    Only one file can be given. -p indicates it is a patch,
61  *        otherwise it is a merge.
62  *    One of the flags -1 -2 or -3 must also be given and they indicate which
63  *    part of the patch or merge to extract.
64  *
65  * Difference calculation and merging is performed on lines (-l) or words (-w).
66  * Each 'word' is either 1/all alphnumeric (or '_'), 2/ all space or tab,
67  * or 3/ any other single character.
68  *
69  * In the case of -w, an initial diff is computed based on non-trivial words
70  * which includes alhpanumeric words and newlines.
71  *
72  * This diff is computed from the ends of the file and is used to find
73  * a suitable starting point and range.  Then a more precise diff is
74  * computed over that restricted range
75  *
76  * Other options available are:
77  *   --replace -r   replace first file with  result of merge.
78  *   --help -h      provide help
79  *   --version -v   version
80  *
81  * Defaults are --merge --words
82  *
83  */
84 #define _GNU_SOURCE
85 #include        "wiggle.h"
86 #include        <errno.h>
87 #include        <fcntl.h>
88 #include        <unistd.h>
89 #include        <stdlib.h>
90 #include        <stdio.h>
91 #include        <ctype.h>
92
93 char *Cmd = "wiggle";
94 int do_trace = 0;
95
96 void die()
97 {
98         fprintf(stderr, "%s: fatal error\n", Cmd);
99         abort();
100         exit(3);
101 }
102
103 void *xmalloc(int size)
104 {
105         void *rv = malloc(size);
106         if (size && !rv) {
107                 char *msg = "Failed to allocate memory - aborting\n";
108                 write(2, msg, strlen(msg));
109                 exit(3);
110         }
111         return rv;
112 }
113
114 void printword(FILE *f, struct elmnt e)
115 {
116         if (e.start[0])
117                 fprintf(f, "%.*s", e.len, e.start);
118         else {
119                 int a, b, c;
120                 sscanf(e.start+1, "%d %d %d", &a, &b, &c);
121                 fprintf(f, "*** %d,%d **** %d\n", b, c, a);
122         }
123 }
124
125 static void printsep(struct elmnt e1, struct elmnt e2)
126 {
127         int a, b, c, d, e, f;
128         sscanf(e1.start+1, "%d %d %d", &a, &b, &c);
129         sscanf(e2.start+1, "%d %d %d", &d, &e, &f);
130         printf("@@ -%d,%d +%d,%d @@\n", b, c, e, f);
131 }
132
133 static int extract(int argc, char *argv[], int ispatch, int which)
134 {
135         /* extract a branch of a diff or diff3 or merge output
136          * We need one file
137          */
138         struct stream f, flist[3];
139
140         if (argc == 0) {
141                 fprintf(stderr,
142                         "%s: no file given for --extract\n", Cmd);
143                 return 2;
144         }
145         if (argc > 1) {
146                 fprintf(stderr,
147                         "%s: only give one file for --extract\n", Cmd);
148                 return 2;
149         }
150         f = load_file(argv[0]);
151         if (f.body == NULL) {
152                 fprintf(stderr,
153                         "%s: cannot load file '%s' - %s\n", Cmd,
154                         argv[0], strerror(errno));
155                 return 2;
156         }
157         if (ispatch) {
158                 if (split_patch(f, &flist[0], &flist[1]) == 0) {
159                         fprintf(stderr,
160                                 "%s: No chunk found in patch: %s\n", Cmd,
161                                 argv[0]);
162                         return 0;
163                 }
164         } else {
165                 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
166                         fprintf(stderr,
167                                 "%s: merge file %s looks bad.\n", Cmd,
168                                 argv[0]);
169                         return 2;
170                 }
171         }
172         if (flist[which-'1'].body == NULL) {
173                 fprintf(stderr,
174                         "%s: %s has no -%c component.\n", Cmd,
175                         argv[0], which);
176                 return 2;
177         } else {
178                 if (write(1, flist[which-'1'].body,
179                           flist[which-'1'].len)
180                     != flist[which-'1'].len)
181                         return 2;
182         }
183         return 0;
184 }
185
186 static int do_diff_lines(struct file fl[2], struct csl *csl)
187 {
188         int a, b;
189         int exit_status = 0;
190         a = b = 0;
191         while (a < fl[0].elcnt || b < fl[1].elcnt) {
192                 if (a < csl->a) {
193                         if (fl[0].list[a].start[0]) {
194                                 printf("-");
195                                 printword(stdout,
196                                           fl[0].list[a]);
197                         }
198                         a++;
199                         exit_status++;
200                 } else if (b < csl->b) {
201                         if (fl[1].list[b].start[0]) {
202                                 printf("+");
203                                 printword(stdout,
204                                           fl[1].list[b]);
205                         }
206                         b++;
207                         exit_status++;
208                 } else {
209                         if (fl[0].list[a].start[0] == '\0')
210                                 printsep(fl[0].list[a],
211                                          fl[1].list[b]);
212                         else {
213                                 printf(" ");
214                                 printword(stdout,
215                                           fl[0].list[a]);
216                         }
217                         a++;
218                         b++;
219                         if (a >= csl->a+csl->len)
220                                 csl++;
221                 }
222         }
223         return exit_status;
224 }
225
226 static int do_diff_words(struct file fl[2], struct csl *csl)
227 {
228         int a, b;
229         int exit_status  = 0;
230         int sol = 1; /* start of line */
231         a = b = 0;
232         while (a < fl[0].elcnt || b < fl[1].elcnt) {
233                 if (a < csl->a) {
234                         exit_status++;
235                         if (sol) {
236                                 int a1;
237                                 /* If we remove a
238                                  * whole line, output
239                                  * +line else clear
240                                  * sol and retry */
241                                 sol = 0;
242                                 for (a1 = a; a1 < csl->a ; a1++)
243                                         if (ends_line(fl[0].list[a1])) {
244                                                 sol = 1;
245                                                 break;
246                                         }
247                                 if (sol) {
248                                         printf("-");
249                                         for (; a < csl->a ; a++) {
250                                                 printword(stdout, fl[0].list[a]);
251                                                 if (ends_line(fl[0].list[a])) {
252                                                         a++;
253                                                         break;
254                                                 }
255                                         }
256                                 } else
257                                         printf("|");
258                         }
259                         if (!sol) {
260                                 printf("<<<--");
261                                 do {
262                                         if (sol)
263                                                 printf("|");
264                                         printword(stdout, fl[0].list[a]);
265                                         sol = ends_line(fl[0].list[a]);
266                                         a++;
267                                 } while (a < csl->a);
268                                 printf("%s-->>>", sol ? "|" : "");
269                                 sol = 0;
270                         }
271                 } else if (b < csl->b) {
272                         exit_status++;
273                         if (sol) {
274                                 int b1;
275                                 sol = 0;
276                                 for (b1 = b; b1 < csl->b; b1++)
277                                         if (ends_line(fl[1].list[b1])) {
278                                                 sol = 1;
279                                                 break;
280                                         }
281                                 if (sol) {
282                                         printf("+");
283                                         for (; b < csl->b ; b++) {
284                                                 printword(stdout, fl[1].list[b]);
285                                                 if (ends_line(fl[1].list[b])) {
286                                                         b++;
287                                                         break;
288                                                 }
289                                         }
290                                 } else
291                                         printf("|");
292                         }
293                         if (!sol) {
294                                 printf("<<<++");
295                                 do {
296                                         if (sol)
297                                                 printf("|");
298                                         printword(stdout, fl[1].list[b]);
299                                         sol = ends_line(fl[1].list[b]);
300                                         b++;
301                                 } while (b < csl->b);
302                                 printf("%s++>>>", sol ? "|" : "");
303                                 sol = 0;
304                         }
305                 } else {
306                         if (sol) {
307                                 int a1;
308                                 sol = 0;
309                                 for (a1 = a; a1 < csl->a+csl->len; a1++)
310                                         if (ends_line(fl[0].list[a1]))
311                                                 sol = 1;
312                                 if (sol) {
313                                         if (fl[0].list[a].start[0]) {
314                                                 printf(" ");
315                                                 for (; a < csl->a+csl->len; a++, b++) {
316                                                         printword(stdout, fl[0].list[a]);
317                                                         if (ends_line(fl[0].list[a])) {
318                                                                 a++, b++;
319                                                                 break;
320                                                         }
321                                                 }
322                                         } else {
323                                                 printsep(fl[0].list[a], fl[1].list[b]);
324                                                 a++; b++;
325                                         }
326                                 } else
327                                         printf("|");
328                         }
329                         if (!sol) {
330                                 printword(stdout, fl[0].list[a]);
331                                 if (ends_line(fl[0].list[a]))
332                                         sol = 1;
333                                 a++;
334                                 b++;
335                         }
336                         if (a >= csl->a+csl->len)
337                                 csl++;
338                 }
339         }
340         return exit_status;
341 }
342
343 static int do_diff(int argc, char *argv[], int obj, int ispatch,
344                    int which, int reverse)
345 {
346         /* create a diff (line or char) of two streams */
347         struct stream f, flist[3];
348         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
349         int exit_status = 0;
350         struct file fl[2];
351         struct csl *csl;
352
353         switch (argc) {
354         case 0:
355                 fprintf(stderr, "%s: no file given for --diff\n", Cmd);
356                 return 2;
357         case 1:
358                 f = load_file(argv[0]);
359                 if (f.body == NULL) {
360                         fprintf(stderr,
361                                 "%s: cannot load file '%s' - %s\n", Cmd,
362                                 argv[0], strerror(errno));
363                         return 2;
364                 }
365                 chunks1 = chunks2 =
366                         split_patch(f, &flist[0], &flist[1]);
367                 if (!flist[0].body || !flist[1].body) {
368                         fprintf(stderr,
369                                 "%s: couldn't parse patch %s\n", Cmd,
370                                 argv[0]);
371                         return 2;
372                 }
373                 break;
374         case 2:
375                 flist[0] = load_file(argv[0]);
376                 if (flist[0].body == NULL) {
377                         fprintf(stderr,
378                                 "%s: cannot load file '%s' - %s\n", Cmd,
379                                 argv[0], strerror(errno));
380                         return 2;
381                 }
382                 if (ispatch) {
383                         f = load_file(argv[1]);
384                         if (f.body == NULL) {
385                                 fprintf(stderr,
386                                         "%s: cannot load patch"
387                                         " '%s' - %s\n", Cmd,
388                                         argv[1], strerror(errno));
389                                 return 2;
390                         }
391                         if (which == '2')
392                                 chunks2 = chunks3 =
393                                         split_patch(f, &flist[2],
394                                                     &flist[1]);
395                         else
396                                 chunks2 = chunks3 =
397                                         split_patch(f, &flist[1],
398                                                     &flist[2]);
399
400                 } else
401                         flist[1] = load_file(argv[1]);
402                 if (flist[1].body == NULL) {
403                         fprintf(stderr,
404                                 "%s: cannot load file"
405                                 " '%s' - %s\n", Cmd,
406                                 argv[1], strerror(errno));
407                         return 2;
408                 }
409                 break;
410         default:
411                 fprintf(stderr,
412                         "%s: too many files given for --diff\n", Cmd);
413                 return 2;
414         }
415         if (reverse) {
416                 f = flist[1];
417                 flist[1] = flist[2];
418                 flist[2] = f;
419         }
420         fl[0] = split_stream(flist[0], obj == 'l' ? ByLine : ByWord);
421         fl[1] = split_stream(flist[1], obj == 'l' ? ByLine : ByWord);
422         if (chunks2 && !chunks1)
423                 csl = pdiff(fl[0], fl[1], chunks2);
424         else
425                 csl = diff(fl[0], fl[1]);
426         if (obj == 'l') {
427                 if (!chunks1)
428                         printf("@@ -1,%d +1,%d @@\n",
429                                fl[0].elcnt, fl[1].elcnt);
430                 exit_status = do_diff_lines(fl, csl);
431         } else {
432                 if (!chunks1) {
433                         /* count lines in each file */
434                         int l1, l2, i;
435                         l1 = l2 = 0;
436                         for (i = 0 ; i < fl[0].elcnt ; i++)
437                                 if (ends_line(fl[0].list[i]))
438                                         l1++;
439                         for (i = 0 ; i < fl[1].elcnt ; i++)
440                                 if (ends_line(fl[1].list[i]))
441                                         l2++;
442                         printf("@@ -1,%d +1,%d @@\n", l1, l2);
443                 }
444                 exit_status = do_diff_words(fl, csl);
445         }
446         return exit_status;
447 }
448
449 static int do_merge(int argc, char *argv[], int obj,
450                     int reverse, int replace, int ignore, int show_wiggles,
451                     int quiet)
452 {
453         /* merge three files, A B C, so changed between B and C get made to A
454          */
455         struct stream f, flist[3];
456         struct file fl[3];
457         int i;
458         int chunks1 = 0, chunks2 = 0, chunks3 = 0;
459         char *replacename = NULL, *orignew = NULL;
460         struct csl *csl1, *csl2;
461         struct ci ci;
462         FILE *outfile = stdout;
463
464         switch (argc) {
465         case 0:
466                 fprintf(stderr, "%s: no files given for --merge\n", Cmd);
467                 return 2;
468         case 3:
469         case 2:
470         case 1:
471                 for (i = 0; i < argc; i++) {
472                         flist[i] = load_file(argv[i]);
473                         if (flist[i].body == NULL) {
474                                 fprintf(stderr, "%s: cannot load file '%s' - %s\n",
475                                         Cmd,
476                                         argv[i], strerror(errno));
477                                 return 2;
478                         }
479                 }
480                 break;
481         default:
482                 fprintf(stderr, "%s: too many files given for --merge\n",
483                         Cmd);
484                 return 2;
485         }
486         switch (argc) {
487         case 1: /* a merge file */
488                 f = flist[0];
489                 if (!split_merge(f, &flist[0], &flist[1], &flist[2])) {
490                         fprintf(stderr, "%s: merge file %s looks bad.\n",
491                                 Cmd,
492                                 argv[0]);
493                         return 2;
494                 }
495                 break;
496         case 2: /* a file and a patch */
497                 f = flist[1];
498                 chunks2 = chunks3 = split_patch(f, &flist[1], &flist[2]);
499                 break;
500         case 3: /* three separate files */
501                 break;
502         }
503         if (reverse) {
504                 f = flist[1];
505                 flist[1] = flist[2];
506                 flist[2] = f;
507         }
508
509         for (i = 0; i < 3; i++) {
510                 if (flist[i].body == NULL) {
511                         fprintf(stderr, "%s: file %d missing\n", Cmd, i);
512                         return 2;
513                 }
514         }
515         if (replace) {
516                 int fd;
517                 replacename = xmalloc(strlen(argv[0]) + 20);
518                 orignew = xmalloc(strlen(argv[0]) + 20);
519                 strcpy(replacename, argv[0]);
520                 strcpy(orignew, argv[0]);
521                 strcat(orignew, ".porig");
522                 if (open(orignew, O_RDONLY) >= 0 ||
523                     errno != ENOENT) {
524                         fprintf(stderr, "%s: %s already exists\n",
525                                 Cmd,
526                                 orignew);
527                         return 2;
528                 }
529                 strcat(replacename, "XXXXXX");
530                 fd = mkstemp(replacename);
531                 if (fd == -1) {
532                         fprintf(stderr,
533                                 "%s: could not create temporary file for %s\n",
534                                 Cmd,
535                                 replacename);
536                         return 2;
537                 }
538                 outfile = fdopen(fd, "w");
539         }
540
541         if (obj == 'l') {
542                 fl[0] = split_stream(flist[0], ByLine);
543                 fl[1] = split_stream(flist[1], ByLine);
544                 fl[2] = split_stream(flist[2], ByLine);
545         } else {
546                 fl[0] = split_stream(flist[0], ByWord);
547                 fl[1] = split_stream(flist[1], ByWord);
548                 fl[2] = split_stream(flist[2], ByWord);
549         }
550         if (chunks2 && !chunks1)
551                 csl1 = pdiff(fl[0], fl[1], chunks2);
552         else
553                 csl1 = diff(fl[0], fl[1]);
554         csl2 = diff(fl[1], fl[2]);
555
556         ci = print_merge2(outfile, &fl[0], &fl[1], &fl[2],
557                           csl1, csl2, obj == 'w',
558                           ignore, show_wiggles);
559         if (!quiet && ci.conflicts)
560                 fprintf(stderr,
561                         "%d unresolved conflict%s found\n",
562                         ci.conflicts,
563                         ci.conflicts == 1 ? "" : "s");
564         if (!quiet && ci.ignored)
565                 fprintf(stderr,
566                         "%d already-applied change%s ignored\n",
567                         ci.ignored,
568                         ci.ignored == 1 ? "" : "s");
569
570         if (replace) {
571                 fclose(outfile);
572                 if (rename(argv[0], orignew) == 0 &&
573                     rename(replacename, argv[0]) == 0)
574                         /* all ok */;
575                 else {
576                         fprintf(stderr,
577                                 "%s: failed to move new file into place.\n",
578                                 Cmd);
579                         return 2;
580                 }
581         }
582         return (ci.conflicts > 0);
583 }
584
585 static int multi_merge(int argc, char *argv[], int obj,
586                        int reverse, int ignore, int show_wiggles,
587                        int replace, int strip,
588                        int quiet)
589 {
590         FILE *f;
591         char *filename;
592         struct plist *pl;
593         int num_patches;
594         int rv = 0;
595         int i;
596
597         if (!replace) {
598                 fprintf(stderr,
599                         "%s: -p in merge mode requires -r\n",
600                         Cmd);
601                 return 2;
602         }
603         if (argc != 1) {
604                 fprintf(stderr,
605                         "%s: -p in merge mode requires exactly one file\n",
606                         Cmd);
607                 return 2;
608         }
609         filename = argv[0];
610         f = fopen(filename, "r");
611         if (!f) {
612                 fprintf(stderr, "%s: cannot open %s\n",
613                         Cmd, filename);
614                 return 2;
615         }
616         pl = parse_patch(f, NULL, &num_patches);
617         fclose(f);
618         if (set_prefix(pl, num_patches, strip) == 0) {
619                 fprintf(stderr, "%s: aborting\n", Cmd);
620                 return 2;
621         }
622         for (i = 0; i < num_patches; i++) {
623                 char *name;
624                 char *av[2];
625                 asprintf(&name, "_wiggle_:%d:%d:%s",
626                          pl[i].start, pl[i].end, filename);
627                 av[0] = pl[i].file;
628                 av[1] = name;
629                 rv |= do_merge(2, av, obj, reverse, 1, ignore,
630                          show_wiggles, quiet);
631         }
632         return rv;
633 }
634
635 int main(int argc, char *argv[])
636 {
637         int opt;
638         int option_index;
639         int mode = 0;
640         int obj = 0;
641         int replace = 0;
642         int which = 0;
643         int ispatch = 0;
644         int reverse = 0;
645         int verbose = 0, quiet = 0;
646         int strip = -1;
647         int exit_status = 0;
648         int ignore = 1;
649         int show_wiggles = 0;
650         char *helpmsg;
651         char *trace;
652
653         trace = getenv("WIGGLE_TRACE");
654         if (trace && *trace)
655                 do_trace = 1;
656
657         while ((opt = getopt_long(argc, argv,
658                                   short_options, long_options,
659                                   &option_index)) != -1)
660                 switch (opt) {
661                 case 'h':
662                         helpmsg = Help;
663                         switch (mode) {
664                         case 'x':
665                                 helpmsg = HelpExtract;
666                                 break;
667                         case 'd':
668                                 helpmsg = HelpDiff;
669                                 break;
670                         case 'm':
671                                 helpmsg = HelpMerge;
672                                 break;
673                         case 'B':
674                                 helpmsg = HelpBrowse;
675                                 break;
676                         }
677                         fputs(helpmsg, stderr);
678                         exit(0);
679
680                 case 'V':
681                         fputs(Version, stderr);
682                         exit(0);
683                 case ':':
684                 case '?':
685                 default:
686                         fputs(Usage, stderr);
687                         exit(2);
688
689                 case 'B':
690                 case 'x':
691                 case 'd':
692                 case 'm':
693                         if (mode == 0) {
694                                 mode = opt;
695                                 continue;
696                         }
697                         fprintf(stderr,
698                                 "%s: mode is '%c' - cannot set to '%c'\n",
699                                 Cmd, mode, opt);
700                         exit(2);
701
702                 case 'w':
703                 case 'l':
704                         if (obj == 0 || obj == opt) {
705                                 obj = opt;
706                                 continue;
707                         }
708                         fprintf(stderr,
709                                 "%s: cannot select both words and lines.\n", Cmd);
710                         exit(2);
711
712                 case 'r':
713                         replace = 1;
714                         continue;
715                 case 'R':
716                         reverse = 1;
717                         continue;
718
719                 case 'i':
720                         ignore = 0;
721                         continue;
722                 case 'W':
723                         show_wiggles = 1;
724                         ignore = 0;
725                         continue;
726
727                 case '1':
728                 case '2':
729                 case '3':
730                         if (which == 0 || which == opt) {
731                                 which = opt;
732                                 continue;
733                         }
734                         fprintf(stderr,
735                                 "%s: can only select one of -1, -2, -3\n", Cmd);
736                         exit(2);
737
738                 case 'p': /* 'patch' or 'strip' */
739                         if (optarg)
740                                 strip = atol(optarg);
741                         ispatch = 1;
742                         continue;
743
744                 case 'v':
745                         verbose++;
746                         continue;
747                 case 'q':
748                         quiet = 1;
749                         continue;
750                 }
751         if (!mode)
752                 mode = 'm';
753
754         if (mode == 'B') {
755                 vpatch(argc-optind, argv+optind, ispatch,
756                        strip, reverse, replace);
757                 /* should not return */
758                 exit(1);
759         }
760
761         if (obj && mode == 'x') {
762                 fprintf(stderr,
763                         "%s: cannot specify --line or --word with --extract\n",
764                         Cmd);
765                 exit(2);
766         }
767         if (mode != 'm' && !obj)
768                 obj = 'w';
769         if (replace && mode != 'm') {
770                 fprintf(stderr,
771                         "%s: --replace only allowed with --merge\n", Cmd);
772                 exit(2);
773         }
774         if (mode == 'x' && !which) {
775                 fprintf(stderr,
776                         "%s: must specify -1, -2 or -3 with --extract\n", Cmd);
777                 exit(2);
778         }
779         if (mode != 'x' && mode != 'd' && which) {
780                 fprintf(stderr,
781                         "%s: -1, -2 or -3 only allowed with --extract or --diff\n",
782                         Cmd);
783                 exit(2);
784         }
785
786         if (ispatch && which == '3') {
787                 fprintf(stderr,
788                         "%s: cannot extract -3 from a patch.\n", Cmd);
789                 exit(2);
790         }
791
792         switch (mode) {
793         case 'x':
794                 exit_status = extract(argc-optind, argv+optind, ispatch, which);
795                 break;
796         case 'd':
797                 exit_status = do_diff(argc-optind, argv+optind, obj, ispatch, which, reverse);
798                 break;
799         case 'm':
800                 if (ispatch)
801                         exit_status = multi_merge(argc-optind,
802                                                   argv+optind, obj,
803                                                   reverse, ignore,
804                                                   show_wiggles,
805                                                   replace, strip,
806                                                   quiet);
807                 else
808                         exit_status = do_merge(argc-optind, argv+optind, obj, reverse, replace,
809                                                ignore, show_wiggles, quiet);
810                 break;
811         }
812         exit(exit_status);
813 }